﻿using Microsoft.EntityFrameworkCore;

namespace _1对多
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            using (MyContext db = new MyContext())
            {
                /*
                //插入数据
                
                Article article = new Article() { Title = "震惊1", Message = "弯道超车……", };

                //创建Commnet对象时，不需要给它的Article属性赋值
                Comment comment1 = new Comment() { Message = "真的假的？" };
                Comment comment2 = new Comment() { Message = "吃瓜" };
                article.Comments.Add(comment1);
                article.Comments.Add(comment2);

                //也不需要把Comment对象加入到DbContext中，EF Core会顺着关系，自动把Comment对象插入到数据库中
                db.Articles.Add(article);
                await db.SaveChangesAsync();
                */

                /*
                //关联查询：使用Include指定要包含的属性

                //从父（文章）->子（评论）:生成的是Left Join
                var article = db.Articles.Include(x => x.Comments).FirstOrDefault(x => x.Id == 3);

                Console.WriteLine($"Article_Title = {article.Title}, Message = {article.Message}");
                foreach (var comment in article.Comments)
                {
                    Console.WriteLine($"Comment_Id = {comment.Id}, Comment_Message = {comment.Message}");
                }

                Console.WriteLine("\n\n\n==================================\n\n\n");

                //从子->父：生成的是inner join
                var cmt = db.Comments.Include(x => x.Article).FirstOrDefault(x => x.Id == 5);
                Console.WriteLine($"评论：{cmt.Message}, 所属文章：{cmt.Article.Id},{cmt.Article.Title}");


                Console.WriteLine("\n\n\n==================================\n\n\n");

                //使用Select查询指定列(不写Selet则查询所有列)
                var article2 = db.Articles.Select(x => new { x.Id, x.Title, }).FirstOrDefault();
                Console.WriteLine($"Article_Id = {article2.Id}, Article_Title = {article2.Title}");

                //使用Select进行关联查询时，可以不使用Include
                var cmt2 = db.Comments.Where(x => x.Id == 5).Select(x => new { Id = x.Id, ArtcleId = x.Article.Id }).FirstOrDefault();
                Console.WriteLine(cmt2.Id + "," + cmt2.ArtcleId);
                */

                //默认情况下，Comment的外键属性Article在数据库中生成的列为：ArticleId
                //这时，如果想只查询Commens表来获取ArticleId的值，通过上面的方法（Select、Include）生成的
                //SQL，均会关联（join）Articles表，这会造成一点点性能损失。
                //解决方法：（正常情况下，不需要这样解决，因为这样可能会造成额外的错误，而且即使join查询，性能损失也不大）
                //1、在Comment实体中，额外添加一个属性：ArticleId（对应数据库中的外键列列名）
                //2、在实体配置类中，添加HasForeignKey(c=>c.ArticleId)指定这个属性为外键。（不加这句的话，迁移后，数据库中会再生成一列）


                /*
                //单项导航属性：插入数据(关联插入)
                //会自动把两个User插入到数据库表中
                User u1 = new User() { Name = "李总" };
                User u2 = new User() { Name = "牛马1号" };
                Leave leave = new Leave() { Remark = "困了，请假！", Requester = u2, Approver = u1 };

                db.Leaves.Add( leave );
                await db.SaveChangesAsync();
                */

                /*
                //复杂关系查询：查询评论中含有“假”的所有文章

                //方式一：
                //如果想要输出评论，则得添加.Include
                //var articles = db.Articles.Include(x=>x.Comments).Where(x => x.Comments.Any(c => c.Message.Contains("假")));
                var articles = db.Articles.Where(x => x.Comments.Any(c => c.Message.Contains("假")));
                foreach (var artilce in articles)
                {
                    Console.WriteLine($"{artilce.Id}：{artilce.Title}");                    
                }

                //方式二：
                var articles2 = db.Comments.Where(x => x.Message.Contains("假")).Select(x => x.Article).Distinct();
                foreach (var artilce in articles2)
                {
                    Console.WriteLine($"{artilce.Id}：{artilce.Title}");
                }   
                */


                /*
                //异步遍历IQuerable：一般情况下没必要这么做

                //方式一：
                foreach (var item in await db.Articles.ToListAsync())
                {
                    Console.WriteLine(item.Message);
                }

                Console.WriteLine("====================");

                //方式二：
                await foreach (var item in db.Articles.AsAsyncEnumerable())
                {
                    Console.WriteLine(item.Message);
                }
                */

                //全局查询过滤器测试

                var articles = db.Articles.IgnoreQueryFilters().Where(x => x.Id > 4);
                foreach (var a in articles)
                {
                    Console.WriteLine($"{a.Id} : {a.Title}");
                }
            }

            //利用IQuerable的延迟执行，来根据查询条件的不同，动态构建查询
            //MyQuery("超车","假",false,true);

            //执行MyQuery2时会报错：解决方法：MyQuery3()：在返回前把数据加载到内存
            //var arts = MyQuery2();
            //var arts = MyQuery3();
            //foreach (var a in arts)
            //{
            //    Console.WriteLine(a.Title);
            //}

            //嵌套遍历
            //如果去掉连接字符串中的：MultipleActiveResultSets=true，那么执行将会报错。
            //因为这时，ADO.NET Core Provider不支持多个DataReader同时执行
            //加上MultipleActiveResultSets=true后，才能正常运行。
            //这个配置是SQL SERVER 特有，其他数据库不支持。
            //MyQuery4();           



        }

        //利用IQuerable的延迟执行，来根据查询条件的不同，动态构建查询
        static void MyQuery(string searchWord, string commnet, bool searchComment, bool orderByIdDesc)
        {
            using (MyContext db = new MyContext())
            {
                IQueryable<Article> articles = db.Articles;

                if (searchComment)
                {
                    articles = articles.Where(x => x.Message.Contains(searchWord) && x.Comments.Any(c => c.Message.Contains(commnet)));
                }
                else
                {
                    articles = articles.Where(x => x.Message.Contains(searchWord));
                }

                if (orderByIdDesc)
                {
                    articles = articles.OrderByDescending(x => x.Id);
                }

                foreach (var art in articles)
                {
                    Console.WriteLine($"Id:{art.Id}, Title:{art.Title}, Message:{art.Message}");
                }
            }
        }

        //调用这个方法，会报错，因为IQueryable是延迟执行，实际遍历时Context已经被销毁了
        static IQueryable<Article> MyQuery2()
        {
            using (var db = new MyContext())
            {
                return db.Articles;
            }
        }

        //解决MyQuery2报错问题：使用ToList()将数据一次性加载到内存中
        static IEnumerable<Article> MyQuery3()
        {
            using (var db = new MyContext())
            {
                return db.Articles.ToList();
            }
        }

        //嵌套遍历：
        static void MyQuery4()
        {
            using (var db = new MyContext())
            {
                foreach (var arts in db.Articles)
                {
                    Console.WriteLine(arts.Message);
                    foreach (var c in db.Comments)
                    {
                        Console.WriteLine("\t\t" + c.Message);
                    }

                }
            }
        }
    }
}
